package game.player.pacman;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import game.core.G;
import game.core.Game;
import game.core.Game.DM;
import gui.AbstractPlayer;

public class PacRob3 extends AbstractPlayer {

	private long lastDecision = 0;
	private boolean flee = false;
	private int attackingGhostId;
	public static final int EVENT_HORIZON = 15;

	public int getAttackingGhostId() {
		return attackingGhostId;
	}

	@Override
	public int getAction(Game game, long timeDue) {

		if (isOnEventHorizon(game) && !game.isEdible(confrontingGhost)) {
			return getFleeDirection(game);
		}

		else { // concentrate on pills
			int current = game.getCurPacManLoc();

			int[] activePills = game.getPillIndicesActive();
			int[] activePowerPills = game.getPowerPillIndicesActive();

			int[] targetsArray = new int[activePills.length
					+ activePowerPills.length];

			for (int i = 0; i < activePills.length; i++) {
				targetsArray[i] = activePills[i];
			}

			for (int i = 0; i < activePowerPills.length; i++) {
				targetsArray[activePills.length + i] = activePowerPills[i];
			}

			return game.getNextPacManDir(
					game.getTarget(current, targetsArray, true, G.DM.PATH),
					true, G.DM.PATH);
		}

	}

	@Override
	public String getGroupName() {
		return "test0815";
	}

	public boolean isOnEventHorizon(Game game) {
		int curPacManLoc = game.getCurPacManLoc();

		boolean isOnEventHorizon = false;

		for (int i = 0; i < Game.NUM_GHOSTS; i++) {
			int curGhostLoc = game.getCurGhostLoc(i);
			int distance = game.getPathDistance(curPacManLoc, curGhostLoc);

			if (distance <= EVENT_HORIZON && distance != -1) {
				isOnEventHorizon = true;
				setConfrontingGhost(i);
			}
		}
		return isOnEventHorizon;
	}

	public int changeDirection(Game game) {
		// current direction is not available :-)
		// what happens, if more than one directions are available?
		// -> check if there is a junction and a ghost so that junction can't be
		// used for a flee.
		return 1;
	}

	private int confrontingGhost;

	public void setConfrontingGhost(int confrontingGhost) {
		this.confrontingGhost = confrontingGhost;
	}

	public int getConfrontingGhost() {
		return confrontingGhost;
	}

	private int confrontingDir;

	public void setConfrontingDir(int confrontingDir) {
		this.confrontingDir = confrontingDir;
	}

	public int getConfrontingDir() {
		return confrontingDir;
	}

	public int getFleeDirection(Game game) {
		int[] possiblePacManDirs = game.getPossiblePacManDirs(true);
		int fleeDirection = 0;
		List<Integer> dirList = new ArrayList<Integer>();

		for (int i = 0; i < possiblePacManDirs.length; i++) {
			if (possiblePacManDirs[i] != -1) {
				if (possiblePacManDirs[i] != computeConfrontingDir(game)) {
					dirList.add(possiblePacManDirs[i]);
				}
			}
		}
		// Patch
		int[] junctionIndices = game.getJunctionIndices();

		// junction mit kürzester Distanz finden
		Map<Integer, Integer> map = new HashMap<Integer, Integer>();
		ValueComparator vc = new ValueComparator(map);
		TreeMap<Integer, Integer> distances = new TreeMap<Integer, Integer>(vc);
		int curPacManLoc = game.getCurPacManLoc();

		for (int i = 0; i < junctionIndices.length; i++) {
			int distance = game.getManhattenDistance(curPacManLoc,
					junctionIndices[i]);
			map.put(i, distance);
		}
		// Now distances is sorted by values.
		distances.putAll(map);

		// first junction
		// gibt es irgendeinen Geist, der in der nähe dieser Kreuzung ist und
		// der Weg enthält keine weitere junction
		int nearestJunctionIndex = distances.lastKey();

		for (int i = 0; i < Game.NUM_GHOSTS; i++) {
			int curGhostLoc = game.getCurGhostLoc(i);
			int[] path = game.getPath(nearestJunctionIndex, curGhostLoc);

			if (containesJunction(path, game)) {
				// berechne Zeit bis zur junction bzw Manhattan Distance als
				// Maßgabe
				if (game.getManhattenDistance(game.getCurPacManLoc(),
						getJunction()) > game.getManhattenDistance(curGhostLoc,
						getJunction())) {
					fleeDirection = game.getReverse(game.getCurPacManDir());
				} else {
					fleeDirection = dirList.get(0);
				}
			} 

		}

		System.out.println(distances.values());

		// erste junction raussuchen; dann junction löschen, dann zweitnächste
		// junction raussuchen

		// Aufruf tactical flee
		return fleeDirection;
//		return dirList.get(0);
		// erst mal checken in welche Richtung man nicht mehr gehen darf

		// Implementierung um pathDistance wieder zu vergrößern.
		// int[] possiblePacManDirs = game.getPossiblePacManDirs(true);
		// int[] junctionIndices = game.getJunctionIndices();
		//
		// for( int i = 0; i < junctionIndices.length; i++ ) {
		//
		// }
		// game.getPath(from, to) // get next junction ... den pfad und schauen
		// ob ein pacman drauf rumläuft
	}

	public boolean containesJunction(int[] path, Game game) {
		int[] junctionIndices = game.getJunctionIndices();
		boolean isJunction = false;

		for (int i = 0; i < path.length; i++) {
			for (int j = 0; j < junctionIndices.length; j++) {
				if (path[i] == junctionIndices[j]) {
					setJunction(i);
					isJunction = true;
				}
			}
		}
		return isJunction;
	}

	private int junction;

	public void setJunction(int junction) {
		this.junction = junction;
	}

	public int getJunction() {
		return junction;
	}

	public int computeConfrontingDir(Game game) {
		// int confrontingDir = 0;

		int curPacManLoc = game.getCurPacManLoc();
		int curConfrontingGhostLoc = game.getCurGhostLoc(confrontingGhost);

		// feststellen der eigenen richtungen
		int curPacManDir = game.getCurPacManDir();
		int curConfrontingGhostDir = game.getCurGhostDir(confrontingGhost);

		// wenn sie direkt aufeinander zulaufen, dann:
		if (game.getX(curPacManLoc) == game.getX(curConfrontingGhostLoc)
				&& curPacManDir == Game.UP
				&& curConfrontingGhostDir == Game.DOWN) {
			confrontingDir = Game.UP;
		} else if (game.getX(curPacManLoc) == game.getX(curConfrontingGhostLoc)
				&& curPacManDir == Game.DOWN
				&& curConfrontingGhostDir == Game.UP) {
			confrontingDir = Game.DOWN;
		} else if (game.getY(curPacManLoc) == game.getY(curConfrontingGhostLoc)
				&& curPacManDir == Game.LEFT
				&& curConfrontingGhostDir == Game.RIGHT) {
			confrontingDir = Game.LEFT;
		} else if (game.getY(curPacManLoc) == game.getY(curConfrontingGhostLoc)
				&& curPacManDir == Game.RIGHT
				&& curConfrontingGhostDir == Game.LEFT) {
			confrontingDir = Game.RIGHT;
		} else if (game.getY(curPacManLoc) != game.getY(curConfrontingGhostLoc)
				&& game.getX(curPacManLoc) != game.getX(curConfrontingGhostLoc)) {
			if (curPacManDir == Game.UP && curConfrontingGhostDir == Game.LEFT
					|| curPacManDir == Game.UP
					&& curConfrontingGhostDir == Game.RIGHT) {
				confrontingDir = Game.UP;
			} else {
				confrontingDir = Game.DOWN;
			}
		}
		// if (!isCorner(game)) {
		// if (curPacManDir == Game.LEFT
		// && curConfrontingGhostDir == Game.UP
		// || curPacManDir == Game.LEFT
		// && curConfrontingGhostDir == Game.DOWN) {
		// confrontingDir = Game.LEFT;
		// } else {
		// confrontingDir = Game.RIGHT;
		// }
		// }
		// else if (game.getX(curPacManLoc) == game.getX(curConfrontingGhostLoc)
		// && !(curPacManDir == curConfrontingGhostDir)) {
		// if( )
		// }
		// } else if (game.getX(curPacManLoc) ==
		// game.getX(curConfrontingGhostLoc)
		// && !(curPacManDir == curConfrontingGhostDir)) {
		// if (curConfrontingGhostDir == Game.LEFT) {
		// confrontingDir = Game.RIGHT;
		// } else {
		// confrontingDir = Game.LEFT;
		// }
		// } else if (game.getY(curPacManLoc) ==
		// game.getY(curConfrontingGhostLoc)
		// && !(curPacManDir == curConfrontingGhostDir)) {
		// if (curConfrontingGhostDir == Game.UP) {
		// confrontingDir = Game.DOWN;
		// } else {
		// confrontingDir = Game.UP;
		// }
		// Die Richtungen können unterschiedlich sein obwohl x bzw y gleich
		// sind.

		// Jetzt die Fälle, bei denen ein unterschiedliches Y und X
		// vorliegt, also senkrechtes aufeinander zulaufen:

		// wenn die vorgeschriebene Distanz erreicht ist, kann es sein, dass
		// der
		// Geist sich noch um die Ecke befindet

		return confrontingDir;
	}

	public boolean isCorner(Game game) {
		boolean isCorner = false;
		// bei folgenden Kombis: (UP,LEFT), (UP,RIGHT), (DOWN,LEFT),
		// (DOWN,RIGHT)
		int[] possibleDirs = game.getPossiblePacManDirs(true);

		List<Integer> list = new ArrayList<Integer>();

		for (int i = 0; i < possibleDirs.length; i++) {
			if (possibleDirs[i] != -1) {
				list.add(possibleDirs[i]);
			}
		}

		if (list.size() == 2) {
			int fst = list.get(0);
			int snd = list.get(1);

			if ((fst == Game.UP && snd == Game.LEFT)
					|| (fst == Game.UP && snd == Game.RIGHT)
					|| (fst == Game.DOWN && snd == Game.LEFT)
					|| (fst == Game.DOWN && snd == Game.RIGHT)) {
				isCorner = true;
			}
		}
		return isCorner;
	}

	// public int computeConfrontingDir(Game game) {
	// int confrontingGhostLoc = game.getCurGhostLoc(getConfrontingGhost());
	// int curPacManLoc = game.getCurPacManLoc();
	//
	// int direction = 0;
	//
	// if (game.getX(curPacManLoc) == game.getX(confrontingGhostLoc)) {
	// if (game.getY(curPacManLoc) > game.getY(confrontingGhostLoc)) {
	// direction = Game.DOWN;
	// } else {
	// direction = Game.UP;
	// }
	// } else if (game.getY(curPacManLoc) == game.getY(confrontingGhostLoc)) {
	// if (game.getX(curPacManLoc) > game.getX(confrontingGhostLoc)) {
	// direction = Game.RIGHT;
	// } else {
	// direction = Game.LEFT;
	// }
	// }
	//
	// return direction;
	// }

	class ValueComparator implements Comparator<Object> {

		private Map<Integer, Integer> map;

		public ValueComparator(Map<Integer, Integer> map) {
			this.map = map;
		}

		@Override
		public int compare(Object arg0, Object arg1) {
			if (map.get(arg0) < map.get(arg1)) {
				return 1;
			} else if (map.get(arg0) == map.get(arg1)) {
				return 0;
			} else {
				return -1;
			}
		}

	}

}
